#pragma once

#define _USE_MATH_DEFINES		// Math Constants
#include <cmath>
#include <cstdlib>

namespace RayTracer
{

	class CVector3
	{
	public:

		CVector3(void) : x(0.f), y(0.f), z(0.f){};
		CVector3(float X, float Y, float Z) : x(X), y(Y), z(Z) {};

		~CVector3(void) {};

		union
		{
			struct { float x, y, z; };
			struct { float r, g, b; };
		};

		void Set(float X, float Y, float Z)
		{
			x = X;
			y = Y;
			z = Z;
		}

		void Normalize()
		{
			float l = 1.f / Length();
			x *= l;
			y *= l;
			z *= l;
		}

		float Length()
		{
			float res = sqrt(x * x + y * y + z * z);
			return res;
		}

		float Length2()
		{
			float res = x * x + y * y + z * z;
			return res;
		}

		float Dot (const CVector3& v)
		{
			float res = x * v.x + y * v.y + z * v.z;
			return res;
		}

		CVector3 Cross (const CVector3& v)
		{
			CVector3 res = CVector3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);
			return res;
		}

		void operator+= (const CVector3& v)
		{
			x += v.x;
			y += v.y;
			z += v.z;
		}

		void operator-= (const CVector3& v)
		{
			x -= v.x;
			y -= v.y;
			z -= v.z;
		}

		void operator*= (const float& f)
		{
			x *= f;
			y *= f;
			z *= f;
		}

		void operator*= (const CVector3& v)
		{
			x *= v.x;
			y *= v.y;
			z *= v.z;
		}

		CVector3 operator- ()
		{
			return CVector3(-x, -y, -z);
		}

		friend CVector3 operator+ (const CVector3& v1, const CVector3& v2)
		{
			return CVector3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
		}

		friend CVector3 operator- (const CVector3& v1, const CVector3& v2)
		{
			return CVector3(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
		}
		
		friend CVector3 operator* (const CVector3& v1, const CVector3& v2)
		{
			return CVector3(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z);
		}

		friend CVector3 operator* (const CVector3& v, const float& f)
		{
			return CVector3(v.x * f, v.y * f, v.z * f);
		}

		friend CVector3 operator* (const float& f, const CVector3& v)
		{
			return CVector3(v.x * f, v.y * f, v.z * f);
		}

		friend CVector3 Normalize (const CVector3& v)
		{
			float l = 1.f / sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
			return v * l;
		}

		friend float Dot (const CVector3& v1, const CVector3& v2)
		{
			float res = v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
			return res;
		}

		friend CVector3 Cross(const CVector3& v1, const CVector3& v2)
		{
			float X = v1.y * v2.z - v1.z * v2.y;
			float Y = v1.z * v2.x - v1.x * v2.z;
			float Z = v1.x * v2.y - v1.y * v2.x;
			return CVector3(X, Y, Z);
		}

		friend float Distance (const CVector3& v1, const CVector3& v2)
		{
			float X = v1.x - v2.x;
			float Y = v1.y - v2.y;
			float Z = v1.z - v2.z;
			float res = sqrt(X * X + Y * Y + Z * Z);
			return res;
		}

		friend float Distance2 (const CVector3& v1, const CVector3& v2)
		{
			float X = v1.x - v2.x;
			float Y = v1.y - v2.y;
			float Z = v1.z - v2.z;
			float res = X * X + Y * Y + Z * Z;
			return res;
		}
	};

}